package pub.tbc.toolkit.http2;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import pub.tbc.toolkit.CloseUtil;
import pub.tbc.toolkit.Loops;
import pub.tbc.toolkit.Objs;
import pub.tbc.toolkit.function.BiConsumer;
import pub.tbc.toolkit.http2.exception.URLErrException;
import pub.tbc.toolkit.http2.exception.UnsupportedContentTypeException;
import pub.tbc.toolkit.xml.SimpleXmlUtil;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.List;
import java.util.Map;

/**
 * @author tbc on 2016/9/18 11:46:00.
 */
@Slf4j
public abstract class AbstractHttpRequester {

    //    private HttpClient httpClient = HttpClients.createDefault();
    private Charset DEFAULT_CHARSET = Charset.forName("utf-8");

    /**
     * 若不设Content-Type，则默认json
     */

    protected final String CONTENT_TYPE_JSON = "application/json";
    protected final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";
    protected final String TEXT_XML = "text/xml";
    //    protected String CONTENT_TYPE_DEFAULT = CONTENT_TYPE_JSON;
    @Setter
    protected String contentType;
    private String bodyTextXml;


    /**
     * URL预处理：若为空则抛异常；若不是以 "http"开头，则添加"http://"
     *
     * @param url 。
     * @return http://+url
     * @author tbc tianbencai@e-eduspace.com
     * @version 1.0 {2015年11月17日 下午3:07:31}
     */
    protected String requestUrlInit(String url) throws URLErrException {
        log.debug("request url = {}", url);
        if (Objs.isEmpty(url)) {
            throw new URLErrException("URL不能为空");
        }
        if (url.startsWith("http://") || url.startsWith("https://")) {
            return url;
        } else {
            log.debug("请求的URL无协议信息，默认http协议，添加：http://");
            return "http://" + url;
        }
    }

    protected String urlParam(Map<String, Object> param) {
        if (Objs.isEmpty(param)) {
            return "";
        }
        final StringBuilder sb = new StringBuilder("?");
        Loops.forEach(param, new BiConsumer<String, Object>() {
            @Override
            public void accept(String k, Object v) {
                if (!sb.toString().equals("?")) sb.append("&");
                sb.append(k).append("=").append(v);
            }
        });
        return sb.toString();
    }

    protected String urlInit(String url, Map<String, Object> param) {
        return requestUrlInit(url) + urlParam(param);
    }

    protected Header[] getHeader(final Map<String, String> headers) {
        if (Objs.isEmpty(headers)) return null;
        log.debug("request headers: {}", headers);
        final List<Header> headerList = Lists.newArrayList();
        Loops.forEach(headers, new BiConsumer<String, String>() {
            @Override
            public void accept(String k, String v) {
                headerList.add(new BasicHeader(k, v));
            }
        });
        return headerList.toArray(new Header[headerList.size()]);
    }

    private List<NameValuePair> bodyConvert(Map<String, Object> param) {
        if (Objs.isEmpty(param)) return null;
        final List<NameValuePair> list = Lists.newArrayList();
        Loops.forEach(param, new BiConsumer<String, Object>() {
            @Override
            public void accept(String k, Object v) {
                list.add(new BasicNameValuePair(k, String.valueOf(v)));
            }
        });
        return list;
    }

    protected HttpEntity getBody(Map<String, Object> body, String contentType) {
        if (Objs.isEmpty(contentType)) return null;
        switch (contentType) {
            case CONTENT_TYPE_JSON:
                return getBodyByJSON(body);
            case CONTENT_TYPE_FORM:
                return getBodyByForm(bodyConvert(body));
            case TEXT_XML:
                return getBodyTextXml(body);
            default:
                throw new UnsupportedContentTypeException("不被支持的ContentType,只支持JSON和FORM");
        }
    }

    protected HttpEntity getBodyByJSON(Map<String, Object> httpBody) {
        AbstractHttpEntity entity = null;
        try {
            entity = new StringEntity(JSON.toJSONString(httpBody), ContentType.APPLICATION_JSON);
        } catch (UnsupportedCharsetException e) {
            log.error("entity=new StringEntity(json, APPLICATION_JSON),UnsupportedCharsetException");
            log.error(e.getMessage());
            throw new UnsupportedCharsetException("不被支持的编码类型");
        }
        log.debug("request body: {}", entity);
        log.debug("request body content: {}", httpBody);
        return entity;
    }

    public HttpEntity getBodyTextXml(Map<String, Object> httpBody) {
        AbstractHttpEntity entity = null;
        String xml = SimpleXmlUtil.map2xml(httpBody);
        log.info("http request-body : {} ", xml);
        try {
            entity = new StringEntity(xml, ContentType.create("text/xml", Consts.UTF_8));
        } catch (UnsupportedCharsetException e) {
            log.error("entity=new StringEntity(SimpleXmlUtil.map2xml(map)...), UnsupportedCharsetException");
            log.error("exception : {}", e.getMessage());
            throw new UnsupportedCharsetException("不被支持的编码类型");
        }
        log.debug("request body: {}", entity);
        log.debug("request body content: {}", httpBody);
        return entity;
    }

    protected HttpEntity getBodyByForm(List<NameValuePair> httpBody) {
        AbstractHttpEntity entity = null;
        entity = new UrlEncodedFormEntity(httpBody, Consts.UTF_8);
        log.debug("request body: " + entity);
        log.debug("request body content: {}", httpBody);
        return entity;
    }

    ResponseWrapper send(HttpUriRequest httpRequest) throws RuntimeException {
        HttpClient httpClient = HttpClients.createDefault();
        HttpResponse response = null;
        try {
            response = httpClient.execute(httpRequest);
        } catch (IOException e) {
            log.error("请求出错: {}", e.getMessage(), e);
            throw new RuntimeException(e);
        }
        StatusLine statusLine = response.getStatusLine();
        int httpCode = statusLine.getStatusCode();
        HttpEntity entity = response.getEntity();
        InputStream inputStream = null;
        try {
            inputStream = entity.getContent();
        } catch (IOException e) {
            log.error("读取返回流出错: {}", e.getMessage(), e);
            throw new RuntimeException("读取HTTP返回流出错:" + e.getMessage(), e);
        }
        StringBuilder sb = new StringBuilder();
        // 默认用UTF-8解码
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, DEFAULT_CHARSET));
        String str = null;
        String msg = "";
        try {
            while ((str = reader.readLine()) != null) {
                sb.append(str);
            }
        } catch (IOException e) {
            msg = "从流中读取内容失败[IOException: " + e.getMessage() + "]";
//            for (StackTraceElement s : e.getStackTrace()) {
//                log.error(s.toString());
//            }
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            CloseUtil.close(inputStream);
        }
        log.info("http response : {}", sb.toString());

        // 设置返回的ContentType
        String contentType = "";
        Header contentTypeHeader = entity.getContentType();
        if (Objs.nonEmpty(contentTypeHeader))
            contentType = contentTypeHeader.getValue();

        return ResponseWrapper.builder().httpCode(200).message(msg).contentType(contentType).httpResponse(response).result(sb.toString()).build();
    }

}
